package com.recharge.shiro;

import com.recharge.common.result.ResultDO;
import com.recharge.common.util.Constant;
import com.recharge.common.util.RedisUtil;
import com.recharge.domain.context.ShiroUtils;
import com.recharge.domain.vo.PermissionDO;
import com.recharge.domain.vo.RoleDO;
import com.recharge.domain.vo.UserLoginDO;
import com.recharge.service.RoleService;
import com.recharge.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 鉴权类 用户密码验证权限加载
 */
public class CustomRealm extends AuthorizingRealm {

	private Logger logger = LoggerFactory.getLogger(this.getClass().getName());

	@Autowired
	private UserService userService;

	@Autowired
	private RoleService roleService;

	@Autowired
	RedisUtil redisUtil;

	@Override
	public String getName() {
		return "shiroRealm";
	}

	/**
	 * 赋予角色和权限:用户进行权限验证时 Shiro会去缓存中找,如果查不到数据,会执行这个方法去查权限,并放入缓存中
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		UserLoginDO user = (UserLoginDO) principalCollection.getPrimaryPrincipal();
		String username = user.getUsername();
//		ResultDO result = roleService.getRoleByUserId(user.getId());

		ResultDO result = roleService.getRolePermissionByUserId(user.getId());//.getRoleByUserId(user.getId());

		logger.debug("用户名:" + username + " 加载用户的角色与权限");
		if (!result.isSuccess()) {
			logger.debug("用户名:" + username + " 角色加载失败");
			logger.debug("error code：" + result.getErrorCode().getCode() + " msg" + result.getErrorCode().getMsg());
			throw new UnknownAccountException("登陆失败---角色加载失败");
		}
		Set<String> roleNames = new TreeSet<>();
		Set<String> permissionNames = new TreeSet<>();
		List<RoleDO> roleList = (List<RoleDO>) result.getModel(ResultDO.FIRST_MODEL_KEY);
		List<PermissionDO> permissionList = (List<PermissionDO>) result.getModel(ResultDO.SECOND_MODEL_KEY);
		if(roleList != null && !roleList.isEmpty()){
			for (RoleDO domain : roleList) {
				String roleCode = domain.getCode();
				roleNames.add(roleCode);
				if(domain.getPermissiones() != null && !domain.getPermissiones().isEmpty()){
					for (PermissionDO domainP : domain.getPermissiones()) {
						if(domainP == null){
							continue;
						}
						if(StringUtils.isEmpty(domainP.getCode())){
							continue;
						}
						permissionNames.add(domainP.getCode());
					}
				}
			}
		}

		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		simpleAuthorizationInfo.setRoles(roleNames);
		simpleAuthorizationInfo.setStringPermissions(permissionNames);
		logger.debug("--------------- 赋予角色和权限成功！ ---------------");
		return simpleAuthorizationInfo;
	}

	/**
	 * 身份认证 - 之后走上面的 授权
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		UsernamePasswordToken tokenInfo = (UsernamePasswordToken)authenticationToken;
		// 获取用户输入的账号
		String username = tokenInfo.getUsername();
		//获取用户信息
		// 通过username从数据库中查找 User对象，如果找到进行验证
		// 实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法
		logger.debug("用户名:" + username + " 用户登录到系统进行鉴权");
		ResultDO result = userService.getUserByName(username);

		if (!result.isSuccess()) {
			logger.debug("用户名:" + username + " 登陆失败");
			throw new UnknownAccountException("登陆失败");
		}
        UserLoginDO domain = (UserLoginDO) result.getModel(ResultDO.FIRST_MODEL_KEY);
        Integer loginFailurCount = (Integer) result.getModel(ResultDO.SECOND_MODEL_KEY);
        if(loginFailurCount > 5) {
            throw new LockedAccountException("登陆失败超过5次锁定用户");
        }
        //判断用户是否锁定
		if(domain.getStatus() == Constant.USER_LOCK){
			throw new LockedAccountException("用户已锁定");
		}
        logger.debug("用户名:" + username + " 开始验证用户密码");
		SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(domain, domain.getPassword(), ByteSource.Util.bytes(domain.getSalt()), getName());
		// 验证成功开始踢人(清除缓存和Session)
		ShiroUtils.deleteCache(username,true);
		// 认证成功后更新token
		String token = ShiroUtils.getSession().getId().toString();//SecurityUtils.getSubject().getSession().getId().toString();
		domain.setToken( token );
		redisUtil.set(Constant.USER + domain.getUsername(), domain);
		return authenticationInfo;
	}
}
